package com.example.demo.suanfa;

import java.util.LinkedList;
import java.util.Random;

/* ━━━━━━佛祖保佑━━━━━━
 *                  ,;,,;
 *                ,;;'(    社
 *      __      ,;;' ' \   会
 *   /'  '\'~~'~' \ /'\.)  主
 * ,;(      )    /  |.     义
 *,;' \    /-.,,(   ) \    码
 *     ) /       ) / )|    农
 *     ||        ||  \)
 *     (_\       (_\
 * ━━━━━━永无BUG━━━━━━
 * @author ：zjq
 * @date ：2021/4/26 10:28
 * @description: TODO
 *               固定窗格的  滑动时间窗口限流实现  用于 sentinel 限流
 *               假设某个服务最多只能每秒钟处理100个请求，我们可以设置一个1秒钟的滑动时间窗口，
 *               窗口中有10个格子，每个格子100毫秒，每100毫秒移动一次，每次移动都需要记录当前服务请求的次数
 *               格子越细，越精确
 * @version: V1.0
 * @slogan: 天下风云出我辈，一入代码岁月催
 */
public class SlidingTimeWindow {

    //服务访问次数，可以放在 Redis 中，实现分布式系统的访问计数
    static Long counter = 0L;

    static boolean flag = false;

    //使用LinkedList来记录滑动窗口的10个格子。
    static LinkedList<Long> slots = new LinkedList<Long>();

    public static void main(String[] args) throws InterruptedException {
        SlidingTimeWindow timeWindow = new SlidingTimeWindow();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    SlidingTimeWindow.doCheck();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        // 主逻辑，模拟 不断有新的请求过来
        while (true) {
            Thread.sleep(1000);

            //TODO 判断限流标记 if else
            if (!flag) {
                System.out.println("不需要限流 ");
                SlidingTimeWindow.counter+=101;  // 模拟每 1 s 有 101 个请求
                Thread.sleep(new Random().nextInt(15));
            } else {
                System.out.println(" @@@@@ 我被限流啦");
            }

        }
    }

    /**
     * 判断 是否 限流
     *
     * @throws InterruptedException
     */
    private static void doCheck() throws InterruptedException {
        // 为了 演示出效果，这边弄了个死循环
        while (true) {
            Thread.sleep(100);
            // 加入slot
            slots.addLast(counter);
            // 初始 10
            if (slots.size() > 10) {
                // 格子大于10  移除 格子
                slots.removeFirst();
            }
            //比较最后一个和第一个，两者相差 100 以上就限流
            if ((slots.peekLast() - slots.peekFirst()) > 100) {
//                System.out.println("限流了。。");
                //TODO 修改限流标记为true
                flag = true;
            } else {
                //TODO 修改限流标记为false
//                System.out.println("不需要限流 ");
                flag = false;
            }

            Thread.sleep(100);
        }
    }


}
